﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web;
using System.Net.Mime;
using System.Net;
using System.ServiceModel.Syndication;
using System.Xml.Linq;


namespace OARepository
{
    /// <summary>
    /// Utility class to store the context of the repository and to serialise
    /// items in various formats.
    /// </summary>
    public class RepoContext
    {
        private SPList list = null;
        private SPListItem item = null;
        private static string[,] dublinCoreMap = new string[,] {
            { "creator",        "246d0907-637c-46b7-9aa0-0bb914daa832" },
            { "date",           "8c06beca-0777-48f7-91c7-6da68bc07b69" },
            { "date",           "28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f" },
            { "description",    "52578fc3-1f01-4f4d-b016-94ccbcf428cf" }
        };

        public RepoContext() : this((string)null, (string)null)
        {
        }
        public RepoContext(string listid) : this(listid, (string)null)
        {
        }
        public RepoContext(string listid, string itemid)
        {
            SPWeb web = SPContext.Current.Web;
            try
            {
                if (listid != null)
                    this.List = web.Lists.GetList(new Guid(listid), true);
            }
            catch (Exception e)
            {
                throw new HttpException((int)HttpStatusCode.NotFound, "List not found", e);
            }
            try
            {
                if (itemid != null)
                    this.Item = this.List.GetItemByUniqueId(new Guid(itemid));
            }
            catch (Exception e)
            {
                throw new HttpException((int)HttpStatusCode.NotFound, "Item not found", e);
            }
        }

        /// <summary>
        /// Get or set the SPList in the repository context.
        /// </summary>
        public SPList List
        {
            get
            {
                return list;
            }
            set
            {
                list = value;
            }
        }

        /// <summary>
        /// Get or set the SPListItem in the repository context.
        /// </summary>
        public SPListItem Item
        {
            get
            {
                return item;
            }
            set
            {
                item = value;
            }
        }

        /// <summary>
        /// Get the lists for the current Web that are suitable for repository treatment.
        /// </summary>
        /// <returns>A dictionary keyed on the list Guid.</returns>
        public Dictionary<Guid, SPList> GetLists()
        {
            Dictionary<Guid, SPList> lists = new Dictionary<Guid, SPList>();
            foreach (SPList list in SPContext.Current.Web.Lists)
            {
                if (list.OnQuickLaunch)
                {
                    lists.Add(list.ID, list);
                }
            }
            return lists;
        }

        /// <summary>
        /// Get the native SharePoint URL for the current list.
        /// </summary>
        /// <returns>The URL as a string.</returns>
        public string ListUrl()
        {
            return this.ListUrl(this.List);
        }
        public string ListUrl(SPList list)
        {
            return SPContext.Current.Web.Url + "/_layouts/OARepository/List.aspx/" + list.ID.ToString();
        }

        /// <summary>
        /// Get the native SharePoint URL for the current item.
        /// </summary>
        /// <returns>The URL as a string.</returns>
        public string ItemUrl()
        {
            return this.ItemUrl(this.Item);
        }
        public string ItemUrl(SPListItem item)
        {
            return SPContext.Current.Web.Url + "/_layouts/OARepository/Item.aspx/" + List.ID.ToString() + "/" + item.UniqueId.ToString();
        }

        /// <summary>
        /// Syndicate the current item (Atom/RSS), including as much metadata as possible.
        /// </summary>
        /// <returns>The item serialised as a SyndicateItem.</returns>
        public SyndicationItem Syndicate()
        {
            return this.Syndicate(this.Item);
        }
        public SyndicationItem Syndicate(SPListItem item)
        {
            XNamespace dublinCoreNS = "http://purl.org/dc/terms/";
            SyndicationItem entry = new SyndicationItem();

            entry.Title = new TextSyndicationContent(item.Title);
            entry.Id = "urn:uuid:" + item.UniqueId.ToString();
            entry.LastUpdatedTime = (DateTime)item[new Guid("28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f")];

            NameValueCollection dc = DublinCore(item);
            foreach (string key in dc.Keys)
            {
                foreach (string value in dc.GetValues(key))
                {
                    entry.ElementExtensions.Add(
                            new XElement(dublinCoreNS + key,
                                new XText(value)
                            )
                        );
                }
            }

            return entry;
        }

        /// <summary>
        /// Get the public facing metadata for the current item rendered using the field
        /// renderers.
        /// </summary>
        /// <returns>A NameValueCollection of HTML metadata.</returns>
        public NameValueCollection Metadata()
        {
            return this.Metadata(this.Item);
        }
        public NameValueCollection Metadata(SPListItem item)
        {
            NameValueCollection data = new NameValueCollection();

            SPContentType type = item.ContentType;

            foreach (SPField field in type.Fields)
            {
                try
                {
                    data.Add(field.Title, field.GetFieldValueAsHtml(item[field.Id]));
                }
                catch (ArgumentException)
                {
                    // ignore missing values
                }
            }

            return data;
        }

        /// <summary>
        /// Get a list of file URLs associated with current item.
        /// </summary>
        /// <returns>A list of URL strings.</returns>
        public List<string> GetFiles()
        {
            return GetFiles(Item);
        }
        public List<string> GetFiles(SPListItem item)
        {
            List<string> list = new List<String>();

            try
            {
                SPAttachmentCollection attachments = item.Attachments;
                lock (this)
                {
                    for (int i = 0; i < attachments.Count; ++i)
                    {
                        list.Add(attachments.UrlPrefix + attachments[i]);
                    }
                }
            }
            catch (ArgumentOutOfRangeException)
            {
                // Document Library
                SPFile file = item.File;
                list.Add(SPContext.Current.Web.Url + file.Url);
            }

            return list;
        }

        /// <summary>
        /// Serialise the current item as Dublin Core.
        /// </summary>
        /// <returns>A NameValueCollection keyed on the unqualified DC term.</returns>
        public NameValueCollection DublinCore()
        {
            return this.DublinCore(this.Item);
        }
        public NameValueCollection DublinCore(SPListItem item)
        {
            NameValueCollection list = new NameValueCollection();

            list.Add("title", item.Title);
            list.Add("relation", this.ItemUrl(item));

            foreach (string url in GetFiles(item))
            {
                list.Add("identifier", url);
            }

            for (int i = 0; i < dublinCoreMap.GetLength(0); ++i)
            {
                try
                {
                    SPField field = item.Fields[new Guid(dublinCoreMap[i, 1])];
                    object value = item[new Guid(dublinCoreMap[i, 1])];
                    switch (field.Type)
                    {
                        case SPFieldType.DateTime:
                            list.Add(dublinCoreMap[i, 0], ((DateTime)value).ToString("yyyy-MM-ddTHH:mm:ssZ"));
                            break;
                        default:
                            list.Add(dublinCoreMap[i, 0], field.GetFieldValueAsText(value));
                            break;
                    }
                    
                    //list.Add(dublinCoreMap[i, 0], item[new Guid(dublinCoreMap[i, 1])].ToString());
                }
                catch (ArgumentException)
                {
                    // ignore missing field
                }
                catch (NullReferenceException)
                {
                    // ignore missing values
                }
            }

            return list;
        }
    }
}
